package com.gxs.ea.fileupload.beans.jsf.outbox;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItem;
import javax.faces.context.FacesContext;

import org.apache.commons.io.IOUtils;
import org.json.JSONObject;
import org.primefaces.component.calendar.Calendar;
import org.primefaces.component.column.Column;
import org.primefaces.component.fieldset.Fieldset;
import org.primefaces.component.inputmask.InputMask;
import org.primefaces.component.inputtext.InputText;
import org.primefaces.component.outputlabel.OutputLabel;
import org.primefaces.component.panelgrid.PanelGrid;
import org.primefaces.component.row.Row;
import org.primefaces.component.selectbooleancheckbox.SelectBooleanCheckbox;
import org.primefaces.component.selectonemenu.SelectOneMenu;
import org.primefaces.model.UploadedFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.gxs.ea.fileportal.domain.BaseCompany;
import com.gxs.ea.fileportal.domain.Field;
import com.gxs.ea.fileupload.beans.data.model.User;
import com.gxs.ea.fileupload.beans.data.service.CompanyService;
import com.gxs.ea.fileupload.beans.data.service.FileMetaCollectionService;
import com.gxs.ea.fileupload.beans.utils.AuthUtils;
import com.gxs.ea.fileupload.beans.utils.JSFUtils;

@Component
@Scope("session")
public class SendBean  implements Serializable 
{
	
	private boolean viewParameterInit = false;
	private static final String FILE_LOCATION = "C:\\temp\\file-upload";
	private static final int NUMBER_OF_FIELD_COLUMNS = 2;
	private UploadedFile file; 
	private Map<String , Object> dynamicFields = new HashMap<String, Object>();
	boolean dynamicViewsDone = false;
	private BaseCompany company = null;
	private BaseCompany gxsCompany = null;
	
	@Autowired
	private JSFUtils jsfUtils;
	
	@Autowired
	private AuthUtils authUtils;
	
	@Autowired
	private CompanyService companyService;
	
	@Autowired
	private FileMetaCollectionService fileMetaCollectionService;

	
	public BaseCompany getGxsCompany() 
	{
		if(gxsCompany == null)
		{
			gxsCompany = this.companyService.findByCompanyCode("gxs");
		}
		return gxsCompany;
	}

	public void setGxsCompany(BaseCompany gxsCompany) {
		this.gxsCompany = gxsCompany;
	}

	public boolean isViewParameterInit() {
		return viewParameterInit;
	}

	public void setViewParameterInit(boolean given) {
		this.viewParameterInit = false;
		
		if(given)
			clearAllData();
	}
	
	private void clearAllData()
	{
		dynamicViewsDone = false;
		dynamicFields = new HashMap<String, Object>();
		company = null;
		file = null;
	}

	public String uploadFileAndData() 
	{  
		if(file != null && file.getFileName() != null && file.getSize() > 0) 
		{  
			InputStream is = null;
			OutputStream os = null;
			try
			{
				String fileId = UUID.randomUUID().toString();
				os = new FileOutputStream(new File(FILE_LOCATION, fileId ) );
				is = file.getInputstream();
				IOUtils.copy(is, os);
				
				saveMongoObject(file.getFileName(), fileId);
				FacesMessage msg = new FacesMessage("Succesful", file.getFileName() + " has been successfully uploaded");  
				FacesContext.getCurrentInstance().addMessage(null, msg);
				dynamicViewsDone = false;
				clearAllData();
			}
			catch(Exception e)
			{
				e.printStackTrace();
				jsfUtils.printError("Error in Upload", "Exception Message: " + e.getMessage());
			}
			finally
			{
				IOUtils.closeQuietly(is);
				IOUtils.closeQuietly(os);
			}
        }  
		else
		{
			jsfUtils.printError("Incomplete Input", "You must select a non-zero size file for uploading");
		}
		
		return "send.xhtml";
	}

	private void saveMongoObject(String originalFileName, String storageId) 
	{
		
		for(String fieldName : dynamicFields.keySet() )
		{
			Field field = findFieldByName(fieldName);
			
			if(field.getFieldSource().equals(Field.FIELD_SOURCE_AUTO))
			{
				if(field.getFieldAutoOptionSelected().equals(Field.AUTO_OPTIONS_COMPANY_CODE))
				{
					dynamicFields.put(fieldName, this.company.getCompanyCode());
				}
				else if(field.getFieldAutoOptionSelected().equals(Field.AUTO_OPTIONS_CURRENT_DATE))
				{
					dynamicFields.put(fieldName, new Date() );
				}
				else if(field.getFieldAutoOptionSelected().equals(Field.AUTO_OPTIONS_CURRENT_TIMESTAMP))
				{
					dynamicFields.put(fieldName, new Date() );
				}
				else if(field.getFieldAutoOptionSelected().equals(Field.AUTO_OPTIONS_CURRENT_USER))
				{
					dynamicFields.put(fieldName, authUtils.getDetailsForLoggedInUser().getUsername() );
				}
				else if(field.getFieldAutoOptionSelected().equals(Field.AUTO_OPTIONS_ORIGINAL_FILENAME))
				{
					dynamicFields.put(fieldName, originalFileName);
				}
			}
			else if(field.getFieldSource().equals(Field.FIELD_SOURCE_EXTRACTED))
			{
				// do nothing
			}
			
			// do required check. 
		}

		if(dynamicFields.get("gxsStatus") != null)
		{
			dynamicFields.put("gxsStatus", "Submitted");
		}

		if(dynamicFields.get("gxsStorageId") != null)
		{
			dynamicFields.put("gxsStorageId", storageId);
		}

		dynamicFields.put("_id", storageId);

		
		JSONObject object = 	new JSONObject(dynamicFields);
		fileMetaCollectionService.insertDBObject(company.getCompanyCode(), object);
	}

	public UploadedFile getFile() {
		return file;
	}

	public void setFile(UploadedFile file) {
		this.file = file;
	}

	public JSFUtils getJsfUtils() {
		return jsfUtils;
	}

	public void setJsfUtils(JSFUtils jsfUtils) {
		this.jsfUtils = jsfUtils;
	}  
	
	public void initDynamicView()
	{
		if(dynamicViewsDone)
			return;
		
		UIComponent container = jsfUtils.findComponent(":mainForm:outputPanel");

		if(container == null)
			return;
		
		container.getChildren().clear();
		
		User user = authUtils.getDetailsForLoggedInUser();
		company = user.getCompany();
		
		if(company == null)
			return;
	
		gxsCompany = this.companyService.findByCompanyCode("gxs");
		
		if(gxsCompany != null)
		{
			List<String> gxsHeadings = gxsCompany.getCompanyMetaConfig().getFieldGropuings();
			
			for(String currentgxsHeading : gxsHeadings)
			{
				List<String> currentGxsFields = gxsCompany.getCompanyMetaConfig().getMapOfOrderedFieldsForInputForm().get(currentgxsHeading);
				
				if(currentGxsFields != null && currentGxsFields.size() > 0)
				{
					List<String> companyFieldsForCurrentGxsHeading = this.company.getCompanyMetaConfig().getMapOfOrderedFieldsForInputForm().get(currentgxsHeading);
					
					if(companyFieldsForCurrentGxsHeading == null)
					{
						companyFieldsForCurrentGxsHeading = new ArrayList<String>();
						this.company.getCompanyMetaConfig().getMapOfOrderedFieldsForInputForm().put(currentgxsHeading, companyFieldsForCurrentGxsHeading );
					}
					
					companyFieldsForCurrentGxsHeading.addAll(currentGxsFields);
				}
			}
		}
		
		for(String heading : company.getCompanyMetaConfig().getFieldGropuings() )
		{
			List<String> fieldsForCurrentHeading = company.getCompanyMetaConfig().getMapOfOrderedFieldsForInputForm().get(heading);
			
			if(fieldsForCurrentHeading == null || fieldsForCurrentHeading.size() < 1)
				continue;
			
			//
			// 	Panel
			Fieldset mainPanel = new Fieldset();
			mainPanel.setStyle("margin-left:10px;margin-bottom:20px;");
			mainPanel.setLegend(heading);
			container.getChildren().add(mainPanel);
		
			addFields(mainPanel, heading, company);
		
		}
		
	}

	private void addFields(UIComponent container, String heading, BaseCompany company) 
	{
		//
		// Add Panel Grid
		PanelGrid panelGrid = new PanelGrid();
		panelGrid.setStyleClass("noBorderGrid");
		container.getChildren().add(panelGrid);
		
		// 
		// Count number of Rows
		
		List<String> fields = company.getCompanyMetaConfig().getMapOfOrderedFieldsForInputForm().get(heading);
		
		int numberOfRows = fields.size() / NUMBER_OF_FIELD_COLUMNS;
		if(fields.size() % NUMBER_OF_FIELD_COLUMNS != 0)
		{
			numberOfRows++;
		}
		
		Iterator<String> fieldIterator = fields.iterator();
		boolean isLastRow = false;
		int partialColumns = fields.size() % NUMBER_OF_FIELD_COLUMNS;
		for(int i=0; i<numberOfRows; i++)
		{
			if(i+1 == numberOfRows)
			{
				isLastRow = true;
			}
			
			Row row = new Row();
			panelGrid.getChildren().add(row);
			int currentColumn = 0;
			
			for(int j=0; j<NUMBER_OF_FIELD_COLUMNS; j++)
			{
				currentColumn++;
				if(isLastRow && partialColumns != 0 && currentColumn > partialColumns)
				{
					 // We need to add Filler columns
					Column labelColumn = new Column();
					row.getChildren().add(labelColumn);
					Column valueColumn = new Column();
					row.getChildren().add(valueColumn);
				}
				else
				{
					String fieldName = fieldIterator.next();
					Field fieldObject = findFieldByName(fieldName);

					Column labelColumn = new Column();
					row.getChildren().add(labelColumn);
					
					OutputLabel label = new OutputLabel();
					label.setValue(fieldObject.getFieldDisplayName());
					labelColumn.getChildren().add(label);
					
					Column valueColumn = new Column();
					row.getChildren().add(valueColumn);
				
					UIComponent inputField = getSingleField(fieldObject);
					valueColumn.getChildren().add(inputField);
				}
				
			}
		}

	}

	
	private UIComponent getSingleField(Field fieldObject) 
	{
		String expressionString = "#{sendBean.dynamicFields['FIELD']}";
		expressionString = expressionString.replace("FIELD", fieldObject.getFieldName());
		
		boolean autoField = fieldObject.getFieldSource().equals(Field.FIELD_SOURCE_AUTO);
		boolean extractedField = fieldObject.getFieldSource().equals(Field.FIELD_SOURCE_EXTRACTED);
		
		if(fieldObject.getFieldType().equals(Field.FIELD_TYPE_STRING))
		{
			if(fieldObject.getFieldSource().equals(Field.FIELD_SOURCE_USER_ENTERED))
			{
				if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
					this.dynamicFields.put(fieldObject.getFieldName(), new String());
				
				
				if(! StringUtils.isEmpty(fieldObject.getInputMask()) )
				{
					InputMask textField = new InputMask();
					textField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
					textField.setStyle("min-width:150px");
			
					if(autoField || extractedField)
					{
						textField.setDisabled(true);
					}
					textField.setMask(fieldObject.getInputMask());
					return textField;
				}
				else
				{
					InputText textField = new InputText();
					textField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
					textField.setStyle("min-width:150px");
			
					if(autoField || extractedField)
					{
						textField.setDisabled(true);
					}
					return textField;
				}
			}
			else if(fieldObject.getFieldSource().equals(Field.FIELD_SOURCE_USER_ENTERED_LOV))
			{
				if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
					this.dynamicFields.put(fieldObject.getFieldName(), new String());
				SelectOneMenu select = new SelectOneMenu();
				select.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
				select.setStyle("min-width:170px");
				
				UISelectItem item = new UISelectItem();
				item.setItemLabel("Select One");
				item.setItemValue("");
				select.getChildren().add(item);

				for(String key : fieldObject.getFieldListOfValueOptions().keySet())
				{
					item = new UISelectItem();
					item.setItemValue(key);
					item.setItemLabel(fieldObject.getFieldListOfValueOptions().get(key));
					select.getChildren().add(item);
				}
				
				return select;
			}
			else
			{
				if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
					this.dynamicFields.put(fieldObject.getFieldName(), new String());
				InputText textField = new InputText();
				textField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
				textField.setStyle("min-width:150px");
			
				if(autoField || extractedField)
				{
					textField.setDisabled(true);
				}
				return textField;
			}
			
		}
		else if(fieldObject.getFieldType().equals(Field.FIELD_TYPE_BOOLEAN))
		{
			if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
				this.dynamicFields.put(fieldObject.getFieldName(), new Boolean(false));
			SelectBooleanCheckbox inputField = new SelectBooleanCheckbox();
			inputField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
			
			if(autoField || extractedField)
			{
				inputField.setDisabled(true);
			}

			return inputField;
			
		}
		else if(fieldObject.getFieldType().equals(Field.FIELD_TYPE_DATE))
		{
			if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
				this.dynamicFields.put(fieldObject.getFieldName(), new Date());
			Calendar inputField = new Calendar();
			inputField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
			
			if(autoField || extractedField)
			{
				inputField.setDisabled(true);
			}

			return inputField;
		}
		else if(fieldObject.getFieldType().equals(Field.FIELD_TYPE_FLOAT))
		{
			if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
				this.dynamicFields.put(fieldObject.getFieldName(), new String());
			InputText textField = new InputText();
			textField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
			textField.setStyle("min-width:150px");
			
			if(autoField || extractedField)
			{
				textField.setDisabled(true);
			}
			return textField;
		}
		else if(fieldObject.getFieldType().equals(Field.FIELD_TYPE_INTEGER))
		{
			if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
				this.dynamicFields.put(fieldObject.getFieldName(), new String());
			InputText textField = new InputText();
			textField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
			textField.setStyle("min-width:150px");
			
			if(autoField || extractedField)
			{
				textField.setDisabled(true);
			}
			
			return textField;
		}
		else if(fieldObject.getFieldType().equals(Field.FIELD_TYPE_TIMESTAMP))
		{
			if(this.dynamicFields.get(fieldObject.getFieldName()) == null)
				this.dynamicFields.put(fieldObject.getFieldName(), new Date());
			
			Calendar inputField = new Calendar();
			inputField.setValueExpression("value", jsfUtils.createValueExpression(expressionString));
			inputField.setPattern("MM/dd/yyyy HH:mm:ss");
			
			if(autoField || extractedField)
			{
				inputField.setDisabled(true);
			}
			
			return inputField;
		}
		else
		{
			return null;
		}
	}

	public CompanyService getCompanyService() {
		return companyService;
	}

	public void setCompanyService(CompanyService companyService) {
		this.companyService = companyService;
	}

	private Field findFieldByName( String fieldName) 
	{

		List<Field> fields = this.company.getCompanyMetaConfig().getFields();
		
		if(fieldName.startsWith("gxs"))
		{
			fields = this.gxsCompany.getCompanyMetaConfig().getFields();
		}
		
		for (Field f : fields)
		{
			if(f.getFieldName().equals(fieldName))
				return f;
		}
		
		return null;
	}

	public Map<String, Object> getDynamicFields() {
		return dynamicFields;
	}

	public void setDynamicFields(Map<String, Object> dynamicFields) {
		this.dynamicFields = dynamicFields;
	}

	public AuthUtils getAuthUtils() {
		return authUtils;
	}

	public void setAuthUtils(AuthUtils authUtils) {
		this.authUtils = authUtils;
	}

	public boolean isDynamicViewsDone() {
		return dynamicViewsDone;
	}

	public void setDynamicViewsDone(boolean dynamicViewsDone) {
		this.dynamicViewsDone = dynamicViewsDone;
	}

	public BaseCompany getCompany() {
		return company;
	}

	public void setCompany(BaseCompany company) {
		this.company = company;
	}

	public FileMetaCollectionService getFileMetaCollectionService() {
		return fileMetaCollectionService;
	}

	public void setFileMetaCollectionService(
			FileMetaCollectionService fileMetaCollectionService) {
		this.fileMetaCollectionService = fileMetaCollectionService;
	}
	

}
